// Muduo - A reactor-based C++ network library for Linux
// Copyright (c) 2010, Shuo Chen.  All rights reserved.
// http://code.google.com/p/muduo/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
//
//   * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//   * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
//   * Neither the name of Shuo Chen nor the names of other contributors
// may be used to endorse or promote products derived from this software
// without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.


#include <clusnet/base/Thread.h>
#include <clusnet/base/CurrentThread.h>
#include <clusnet/base/Exception.h>

#include <string>

#include <unistd.h>
#include <sys/prctl.h>
#include <sys/types.h>
#include <sys/syscall.h>
#include <chrono>
#include <sstream>
#include <cassert>

namespace clusnet
{
namespace base
{

namespace CurrentThread
{
    __thread int    t_cachedTid = 0;
    __thread char   t_tidString[32];
    __thread int    t_tidStringLength = 0;
    __thread const char*    t_threadName = "unknown";
    const bool sameType = std::is_same<int, pid_t>::value;
    static_assert(sameType, "int and pid_t must be same type");
}

namespace detail
{
    pid_t   gettid()
    {
        return static_cast<pid_t>(::syscall(SYS_gettid));
    }

    void runInThread(const clusnet::base::Thread::ThreadFunc& func, const std::string& name, pid_t& tid, CountDownLatch& latch)
    {
        tid = clusnet::base::CurrentThread::tid();
        latch.countDown();

        clusnet::base::CurrentThread::t_threadName = name.empty() ? "clusnetThread" : name.c_str();
        ::prctl(PR_SET_NAME, clusnet::base::CurrentThread::t_threadName);

        try
        {
            func();
            clusnet::base::CurrentThread::t_threadName = "finished";
        }
        catch (const clusnet::base::Exception& ex)
        {
            clusnet::base::CurrentThread::t_threadName = "crashed";
            fprintf(stderr, "exception caught in Thread %s\n", clusnet::base::CurrentThread::t_threadName);
            fprintf(stderr, "reason: %s\n", ex.what());
            fprintf(stderr, "stack trace: %s\n", ex.stackTrace());
            abort();
        }
        catch (const std::exception& ex)
        {
            clusnet::base::CurrentThread::t_threadName = "crashed";
            fprintf(stderr, "exception caught in Thread %s\n", clusnet::base::CurrentThread::t_threadName);
            fprintf(stderr, "reason: %s\n", ex.what());
            abort();
        }
        catch (...)
        {
            clusnet::base::CurrentThread::t_threadName = "crashed";
            fprintf(stderr, "unknown exception caught in Thread %s\n", clusnet::base::CurrentThread::t_threadName);
            throw;
        }
    }
}
}
}

using namespace clusnet::base;

void CurrentThread::cacheTid()
{
    if (t_cachedTid == 0)
    {
        t_cachedTid = detail::gettid();
        t_tidStringLength = snprintf(t_tidString, sizeof t_tidString, "%5d ", t_cachedTid);
    }
}

bool CurrentThread::isMainThread()
{
    return tid() == ::getpid();
}

void CurrentThread::sleepUsec(uint64_t usec)
{
    std::this_thread::sleep_for(std::chrono::nanoseconds(usec));
}

std::atomic_int Thread::numCreated_(0);

class MainThreadNameInitializer final 
{
    public:
        MainThreadNameInitializer()
        {
            clusnet::base::CurrentThread::t_threadName = "main";
            clusnet::base::CurrentThread::tid();
        }
        ~MainThreadNameInitializer() = default;
        MainThreadNameInitializer(const MainThreadNameInitializer&) = delete;
        MainThreadNameInitializer& operator= (const MainThreadNameInitializer&) = delete;
};
MainThreadNameInitializer maininit;

Thread::Thread(const ThreadFunc& func, const std::string& name)
    : started_(false),
      joined_(false),
      thread_(),
      tid_(0),
      func_(func),
      name_(name),
      latch_(1)
{
    setDefaultName();
}

Thread::Thread(ThreadFunc&& func, const std::string& name)
    : started_(false),
      joined_(false),
      thread_(),
      tid_(0),
      func_(std::move(func)),
      name_(name),
      latch_(1)
{
    setDefaultName();
}

Thread::~Thread()
{
    if (started_ && !joined_)
    {
        thread_.detach();
    }
}

void Thread::setDefaultName()
{
    int num = numCreated_.fetch_add(1) + 1;
    if (name_.empty())
    {
        std::stringstream name;
        name << "Thread" << num;
        name_ = name.str();
    }
}

void Thread::start()
{
    assert(!started_);
    started_ = true;
    // 这里会抛异常，不处理，直接让程序结束
    thread_ = std::thread(clusnet::base::detail::runInThread, std::ref(func_), std::ref(name_), std::ref(tid_), std::ref(latch_));
    latch_.wait();
    assert(tid_ > 0);
}

void Thread::join()
{
    assert(started_);
    assert(!joined_);
    joined_ = true;
    thread_.join();
}
